package com.lyl.myaiagent.app;

import com.lyl.myaiagent.advisor.MySimpleLoggerAdvisor;
import com.lyl.myaiagent.advisor.ReReadingAdvisor;
import com.lyl.myaiagent.chatmemory.FileBaseChatMemory;
import com.lyl.myaiagent.servertools.tools.registration.ToolRegistration;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
import org.springframework.ai.chat.client.advisor.RetrievalAugmentationAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.client.advisor.api.Advisor;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;
import org.springframework.ai.image.*;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;

import java.util.List;

import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;
import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY;

/**
 * @author lyl
 * @version 1.0
 * @CreateTime: 2025-08-18
 */
@Component
@Slf4j
public class PROApp {

    @Resource
    private ToolRegistration toolRegistration;

    @Resource
    private ToolCallbackProvider toolCallbackProvider;

    private final ChatClient chatClient;

    private static final String systemPrompt = """
            你是中国海上交通法的专家，开场向用户表明自己的身份，告知用户可回答海上交通相关难题，
            请你根据用户的请求，以你专业的知识，给用户解答疑惑，引导用户详述事情经过，对方
            的反应及自身想法，以便给出专属的解决方案，另外如果你的知识库中没有用户的答案，
            你可以分析用户的需求，调用现有的工具，帮助用户解决问题，注意，不要给用户虚假编造的信息。
            """;


    public PROApp(ChatModel dashscopChatModel, ImageModel dashcopeImageModel){
        // 保存上下文
        // InMemoryChatMemory chatMemory = new InMemoryChatMemory();// 基于内存实现
        String dir = System.getProperty("user.dir") + "/tmp/chat-memory";// 基于文件持久化
        FileBaseChatMemory chatMemory = new FileBaseChatMemory(dir);
        // todo 1. 实现保存上下文，持久化->基于数据库实现
        this.chatClient = ChatClient.builder(dashscopChatModel)
                //.defaultSystem(systemPrompt)
                .defaultAdvisors(
                        new MessageChatMemoryAdvisor(chatMemory),
                        new MySimpleLoggerAdvisor()
                        //new ReReadingAdvisor()//实现re2拦截器
                        // todo 2.实现违禁词校验
                )
                .build();
    }

    /**
     * 流式输出-简单对话
     * @param userPrompt 用户提示词
     * @param chatId 聊天室id
     * @return 流式响应结果
     */
    public Flux<String> simpleDoChat(String userPrompt, String chatId){
        return chatClient.prompt()
                .user(userPrompt)
                .advisors(advisorSpec -> advisorSpec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .stream()
                .content();
    }


    record ProReport(String title, List<String> suggestions){}

    /**
     * 结构化输出-简单对话
     * @param userPrompt 用户提示词
     * @param chatId 聊天室id
     * @return 结构化的响应结果
     */
    public ProReport simpleChatWithReport(String userPrompt, String chatId){

        ProReport report = this.chatClient.prompt()
                .user(userPrompt)
                .advisors(advisorSpec -> advisorSpec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .call()
                .entity(ProReport.class);

        log.info("Report：{}", report);
        return report;
    }

    /********************************下面三种方式三选一即可********************************/

    //@Resource
    private VectorStore proVectorStore;
    /**
     * 基于本地知识库的Rag 问答---[使用本地知识库，内存向量数据库]
     * @param userPrompt
     * @param chatId
     * @return
     */
    public Flux<String> chatWithLocalRag(String userPrompt, String chatId){
        return chatClient.prompt()
                .user(userPrompt)
                .advisors(advisorSpec -> advisorSpec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .advisors(
                        new QuestionAnswerAdvisor(proVectorStore)
                )
                .stream()
                .content();
    }

    //@Resource
    private Advisor proCloudAdvisor;

    /**
     * 基于云知识库的应用增强服务---[使用云知识库，貌似没有使用VectorStore]
     * @param userPrompt 用户提示词
     * @param chatId 聊天室id
     * @return 流式结果
     */
    public Flux<String> chatWithCloudRag(String userPrompt, String chatId){
        return chatClient.prompt()
                .user(userPrompt)
                .advisors(advisorSpec -> advisorSpec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .advisors(proCloudAdvisor)//使用应用增强检索服务（云知识库服务）
                .stream()
                .content();
    }

    @Resource
    private VectorStore pgVectorVectorStore;

    /**
     * 基于本地知识库的Rag 问答---[使用本地知识库，PGSQL向量数据库]
     * @param userPrompt 用户提示词
     * @param chatId 聊天室id
     * @return 流式结果
     */
    public Flux<String> chatWithRagBasePgVector(String userPrompt, String chatId){
        return chatClient.prompt()
                .user(userPrompt)
                .advisors(advisorSpec -> advisorSpec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .advisors(
                        new QuestionAnswerAdvisor(pgVectorVectorStore)
                )//使用应用增强检索服务（云知识库服务）
                .stream()
                .content();
    }

    /**
     * 基于本地知识库的Rag和工具 问答---[使用本地知识库，PGSQL向量数据库]
     * @param userPrompt 用户提示词
     * @param chatId 聊天室id
     * @return 流式结果
     */
    public Flux<String> chatWithToolsAndRagBasePgVector(String userPrompt, String chatId){
        return chatClient.prompt()
                .user(userPrompt)
                .advisors(advisorSpec -> advisorSpec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .advisors(
                        new QuestionAnswerAdvisor(pgVectorVectorStore)
                )//使用应用增强检索服务（云知识库服务）
                .tools(toolRegistration.toolCallbacks())
                .stream()
                .content();
    }

    /**
     * 基于本地知识库的Rag和工具 问答---[使用本地知识库，PGSQL向量数据库]
     * @param userPrompt 用户提示词
     * @param chatId 聊天室id
     * @return 同步结果
     */
    public String chatWithToolsAndRagBasePgVectorString(String userPrompt, String chatId){
        ChatResponse chatResponse = chatClient.prompt()
                .user(userPrompt)
                .advisors(advisorSpec -> advisorSpec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .advisors(
                        new QuestionAnswerAdvisor(pgVectorVectorStore)
                )//使用应用增强检索服务（云知识库服务）
                .tools(toolRegistration.toolCallbacks())
                .call()
                .chatResponse();
        return chatResponse.getResult().getOutput().getText();
    }

    public String chatWithMCP_Tools_RagBasePgVectorString(String userPrompt, String chatId){
        ChatResponse chatResponse = chatClient.prompt()
                .user(userPrompt)
                .advisors(advisorSpec -> advisorSpec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                .advisors(
                        new QuestionAnswerAdvisor(pgVectorVectorStore)
                )//使用应用增强检索服务（云知识库服务）
                .tools(toolRegistration.toolCallbacks())
                .tools(toolCallbackProvider)
                .call()
                .chatResponse();
        String content = chatResponse.getResult().getOutput().getText();
        log.info("content:" + content);
        return content;
    }

}
